<template>
  <div ref="tagCloudRef" class="tag-cloud-container" :style="{ height: height + 'px' }"></div>
</template>

<script setup lang="ts">
import {onBeforeUnmount, onMounted, ref, watch} from 'vue'
import * as THREE from 'three'
import {CSS3DObject, CSS3DRenderer} from 'three/examples/jsm/renderers/CSS3DRenderer.js'

defineOptions({ name: 'TagCloud' })

const props = defineProps({
  tags: {
    type: Array as () => { name: string; url?: string; image?: string }[],
    required: true
  },
  radius: {
    type: Number,
    default: 200
  },
  height: {
    type: Number,
    default: 300
  },
  speed: {
    type: Number,
    default: 1
  },
  color: {
    type: String,
    default: '#409EFF'
  },
  backgroundColor: {
    type: String,
    default: 'transparent'
  },
  imageSize: {
    type: Number,
    default: 60
  },
  // 标签间距系数，值越大间距越大
  spacingFactor: {
    type: Number,
    default: 1.5
  },
  // 是否启用自动旋转
  autoRotate: {
    type: Boolean,
    default: true
  }
})

const tagCloudRef = ref<HTMLElement | null>(null)
let scene: THREE.Scene
let camera: THREE.PerspectiveCamera
let renderer: CSS3DRenderer
let animationFrameId: number
let mouseX = 0
let mouseY = 0
let windowHalfX = window.innerWidth / 2
let windowHalfY = window.innerHeight / 2

// 拖动相关变量
let isDragging = false
let previousMousePosition = { x: 0, y: 0 }
let rotationSpeed = { x: 0, y: 0 }
let targetRotation = { x: 0, y: 0 }
let dampingFactor = 0.95 // 阻尼因子，使旋转逐渐减速

// 创建标签云
const createTagCloud = () => {
  if (!tagCloudRef.value) return

  // 清除之前的内容
  while (tagCloudRef.value.firstChild) {
    tagCloudRef.value.removeChild(tagCloudRef.value.firstChild)
  }

  // 创建场景
  scene = new THREE.Scene()

  // 创建相机
  camera = new THREE.PerspectiveCamera(75, tagCloudRef.value.clientWidth / props.height, 0.1, 1000)
  camera.position.z = props.radius * 1.5

  // 创建渲染器
  renderer = new CSS3DRenderer()
  renderer.setSize(tagCloudRef.value.clientWidth, props.height)
  renderer.domElement.style.position = 'absolute'
  renderer.domElement.style.top = '0'
  renderer.domElement.style.backgroundColor = props.backgroundColor
  tagCloudRef.value.appendChild(renderer.domElement)

  // 创建标签
  const tags = props.tags
  const count = tags.length

  // 使用斐波那契球面分布算法，确保标签均匀分布
  const createFibonacciSphere = (samples: number, radius: number) => {
    const points = []
    const phi = Math.PI * (3 - Math.sqrt(5)) // 黄金角

    for (let i = 0; i < samples; i++) {
      const y = 1 - (i / (samples - 1)) * 2 // y从1到-1
      const radiusAtY = Math.sqrt(1 - y * y) // 在y处的半径

      const theta = phi * i // 黄金角旋转

      const x = Math.cos(theta) * radiusAtY
      const z = Math.sin(theta) * radiusAtY

      points.push({
        x: x * radius * props.spacingFactor,
        y: y * radius * props.spacingFactor,
        z: z * radius * props.spacingFactor
      })
    }

    return points
  }

  const positions = createFibonacciSphere(count, props.radius)

  for (let i = 0; i < count; i++) {
    const tag = tags[i]
    const position = positions[i]

    // 创建HTML元素
    const element = document.createElement('div')
    element.className = 'tag-item'
    element.style.padding = '8px 12px'
    element.style.borderRadius = '8px'
    element.style.cursor = 'pointer'
    element.style.fontSize = '16px'
    element.style.fontWeight = 'bold'
    element.style.color = '#fff'
    element.style.backgroundColor = props.color
    element.style.boxShadow = '0 4px 8px rgba(0,0,0,0.2)'
    element.style.transition = 'transform 0.3s, z-index 0.3s'
    element.style.display = 'flex'
    element.style.alignItems = 'center'
    element.style.justifyContent = 'center'
    element.style.textAlign = 'center'
    element.style.userSelect = 'none'
    element.style.whiteSpace = 'nowrap'
    element.style.zIndex = '10'
    element.style.opacity = '0.9'

    // 如果是图片标签
    if (tag.image) {
      const img = document.createElement('img')
      img.src = tag.image
      img.style.width = `${props.imageSize}px`
      img.style.height = `${props.imageSize}px`
      img.style.borderRadius = '50%'
      img.style.objectFit = 'cover'
      img.style.border = '2px solid white'
      element.appendChild(img)
    } else {
      element.textContent = tag.name
    }

    // 添加点击事件
    if (tag.url) {
      element.addEventListener('click', () => {
        window.open(tag.url, '_blank')
      })
    }

    // 添加鼠标悬停效果
    element.addEventListener('mouseover', () => {
      element.style.transform = 'scale(1.2)'
      element.style.zIndex = '100'
      element.style.opacity = '1'
      element.style.boxShadow = '0 8px 16px rgba(0,0,0,0.3)'
    })
    element.addEventListener('mouseout', () => {
      element.style.transform = 'scale(1)'
      element.style.zIndex = '10'
      element.style.opacity = '0.9'
      element.style.boxShadow = '0 4px 8px rgba(0,0,0,0.2)'
    })

    // 创建3D对象
    const object = new CSS3DObject(element)
    object.position.set(position.x, position.y, position.z)

    // 让标签始终面向相机
    object.lookAt(camera.position)

    scene.add(object)
  }

  // 添加鼠标事件
  document.addEventListener('mousemove', onDocumentMouseMove)
  window.addEventListener('resize', onWindowResize)

  // 添加拖动事件
  if (tagCloudRef.value) {
    tagCloudRef.value.addEventListener('mousedown', onMouseDown)
    document.addEventListener('mouseup', onMouseUp)
    document.addEventListener('mouseleave', onMouseUp)
  }

  // 开始动画
  animate()
}

// 鼠标按下事件
const onMouseDown = (event: MouseEvent) => {
  event.preventDefault()
  isDragging = true
  previousMousePosition = {
    x: event.clientX,
    y: event.clientY
  }

  // 设置鼠标样式为抓取
  if (tagCloudRef.value) {
    tagCloudRef.value.style.cursor = 'grabbing'
  }
}

// 鼠标释放事件
const onMouseUp = () => {
  isDragging = false

  // 恢复鼠标样式
  if (tagCloudRef.value) {
    tagCloudRef.value.style.cursor = 'grab'
  }
}

// 鼠标移动事件
const onDocumentMouseMove = (event: MouseEvent) => {
  mouseX = (event.clientX - windowHalfX) * 0.05
  mouseY = (event.clientY - windowHalfY) * 0.05

  // 处理拖动旋转
  if (isDragging) {
    const deltaMove = {
      x: event.clientX - previousMousePosition.x,
      y: event.clientY - previousMousePosition.y
    }

    // 根据鼠标移动距离计算旋转速度
    rotationSpeed.x = deltaMove.y * 0.01
    rotationSpeed.y = deltaMove.x * 0.01

    // 更新目标旋转角度
    targetRotation.x += rotationSpeed.x
    targetRotation.y += rotationSpeed.y

    previousMousePosition = {
      x: event.clientX,
      y: event.clientY
    }
  }
}

// 窗口大小改变事件
const onWindowResize = () => {
  if (!tagCloudRef.value) return
  windowHalfX = tagCloudRef.value.clientWidth / 2
  windowHalfY = props.height / 2
  camera.aspect = tagCloudRef.value.clientWidth / props.height
  camera.updateProjectionMatrix()
  renderer.setSize(tagCloudRef.value.clientWidth, props.height)
}

// 动画函数
const animate = () => {
  animationFrameId = requestAnimationFrame(animate)

  // 如果启用自动旋转且没有拖动，则自动旋转
  if (props.autoRotate && !isDragging) {
    scene.rotation.y += 0.002 * props.speed
    scene.rotation.x += 0.001 * props.speed
  }

  // 应用拖动旋转
  if (isDragging || Math.abs(rotationSpeed.x) > 0.001 || Math.abs(rotationSpeed.y) > 0.001) {
    // 平滑过渡到目标旋转角度
    scene.rotation.x += (targetRotation.x - scene.rotation.x) * 0.1
    scene.rotation.y += (targetRotation.y - scene.rotation.y) * 0.1

    // 应用阻尼，使旋转逐渐减速
    rotationSpeed.x *= dampingFactor
    rotationSpeed.y *= dampingFactor
  }

  // 根据鼠标位置调整旋转
  if (!isDragging) {
    scene.rotation.x += (mouseY * 0.001 - scene.rotation.x) * 0.05
    scene.rotation.y += (mouseX * 0.001 - scene.rotation.y) * 0.05
  }

  // 更新标签朝向，始终面向相机
  scene.traverse((object) => {
    if (object instanceof CSS3DObject) {
      object.lookAt(camera.position)
    }
  })

  renderer.render(scene, camera)
}

// 监听标签变化
watch(() => props.tags, () => {
  createTagCloud()
}, { deep: true })

// 组件挂载时初始化
onMounted(() => {
  createTagCloud()

  // 设置初始鼠标样式
  if (tagCloudRef.value) {
    tagCloudRef.value.style.cursor = 'grab'
  }
})

// 组件卸载时清理
onBeforeUnmount(() => {
  if (animationFrameId) {
    cancelAnimationFrame(animationFrameId)
  }
  document.removeEventListener('mousemove', onDocumentMouseMove)
  document.removeEventListener('mouseup', onMouseUp)
  document.removeEventListener('mouseleave', onMouseUp)
  window.removeEventListener('resize', onWindowResize)

  if (tagCloudRef.value) {
    tagCloudRef.value.removeEventListener('mousedown', onMouseDown)
  }
})
</script>

<style scoped>
.tag-cloud-container {
  position: relative;
  overflow: hidden;
  width: 100%;
  touch-action: none; /* 防止触摸设备上的默认行为 */
}
</style>

